package com.david.mydemo.util;


import org.geotools.geometry.jts.JTS;
import org.geotools.referencing.CRS;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.WKTReader;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 空间数据处理工具
 */

public class GeometryUtil {

    private static Logger log = LoggerFactory.getLogger(GeometryUtil.class);

    /**
     * 半径转换比例系数 0.0097656 调整为 0.0089993
     **/

    private static final double DISTANCE_CONVERT_RATE = 0.0089993;
    private static double rate = 0.2;

    /**
     * 根据wkt计算多边形面积
     *
     * @param wkt 待计算wkt
     * @return 面积数据
     */

    public static double getAreaWithWkt(String wkt) {
        Geometry geometry;
        try {
            WKTReader reader = new WKTReader();
            geometry = reader.read(wkt);
            double area = geometry.getArea() / (DISTANCE_CONVERT_RATE * DISTANCE_CONVERT_RATE);
            return area;
        } catch (ParseException e) {
            geometry = null;
            log.error("wkt数据转换geometry并获取面积出错。错误信息：", e);
        }
        return 0;
    }

    /**
     * 根据wkt获取Geometry
     *
     * @param wkt 待计算wkt
     * @return 面积数据
     */

    public static Geometry getGeometryWithWkt(String wkt) {
        Geometry geometry;
        try {
            WKTReader reader = new WKTReader();
            geometry = reader.read(wkt);
            return geometry;
        } catch (ParseException e) {
            geometry = null;
            log.error("wkt数据转换geometry出错。错误信息：", e);
        }
        return geometry;
    }

    /**
     * 根据wkt计算多边形面积
     * <p>通过转换墨卡托的方式</p>
     *
     * @param wkt 待计算wkt
     * @return 面积数据
     */

    public static double getAreaByMercatorWithWkt(String wkt) {
        Geometry geometry;
        try {
            WKTReader reader = new WKTReader();
            geometry = reader.read(wkt);
        } catch (ParseException e) {
            geometry = null;
            log.error("wkt数据转换geometry出错。错误信息：", e);
        }
        return getAreaByMercator(geometry);
    }

    /**
     * 根据多边形类型计算出多边形面积，单位（平方米），84坐标系
     * <p>需要先将坐标系转换为墨卡托坐标系</p>
     *
     * @param geometry 多边形对象
     * @return 面积
     */

    public static double getAreaByMercator(Geometry geometry) {
        if (geometry == null) {
            return 0;
        }
        MathTransform transform = null;
        try {
            CoordinateReferenceSystem source = CRS.decode("CRS:84");
            CoordinateReferenceSystem target = CRS.decode("EPSG:3857");
            transform = CRS.findMathTransform(source, target, true);
        } catch (FactoryException e) {
            log.error("获取数学转换对象出错。错误信息：", e);
        }
        try {
            Geometry transformGeometry = JTS.transform(geometry, transform);
            // 转成平方公里
            double area = transformGeometry.getArea() / (1000 * 1000);
            log.info("geometry.area={},transformGeometry.area={}", geometry.getArea(), transformGeometry.getArea());
            return area;
        } catch (TransformException e) {
            log.error("转换geometry出错。错误信息：", e);
        }
        return 0;
    }

    /**
     * 根据wkt获取平滑处理后的Geometry
     *
     * @param wkt 待计算wkt
     * @return 面积数据
     */

    public static Geometry smoothWithWkt(String wkt) {
        Geometry geometry;
        try {
            WKTReader reader = new WKTReader();
            geometry = JTS.smooth(reader.read(wkt), Double.valueOf(rate).doubleValue());
            return geometry;
        } catch (ParseException e) {
            geometry = null;
            log.error("wkt数据转换geometry出错。错误信息：", e);
        }
        return geometry;
    }

    /**
     * 判断点在面内
     *
     * @param wkt       待计算面wkt
     * @param longitude
     * @param latitude
     * @return boolean
     */

    public static boolean pointInPolygonWKT(String wkt, Double longitude, Double latitude) {
        Geometry geometry;
        try {
            WKTReader reader = new WKTReader();
            geometry = reader.read(wkt);
            GeometryFactory geometryFactory = new GeometryFactory();
            Point point = geometryFactory.createPoint(new Coordinate(longitude, latitude));
            return geometry.intersects(point);
        } catch (ParseException e) {
            geometry = null;
            log.error("wkt数据转换geometry出错。错误信息：", e);
        }
        return false;
    }

    public static void main(String[] args) {

        String wkt = "";
        double areaByWkt = 0;
        double areaByMercatorWithWkt = 0;
        // 直径 2公里
        wkt = "POLYGON ((108.95240191820795 34.26903850988079,108.93440331820796 34.26903850988079,108.93440331820796 34.25103990988079,108.95240191820795 34.25103990988079,108.95240191820795 34.26903850988079))";
//        areaByMercatorWithWkt = GeometryUtil.getAreaByMercatorWithWkt(wkt);
        areaByWkt = GeometryUtil.getAreaWithWkt(wkt);
        System.out.println(areaByWkt + "____" + areaByMercatorWithWkt);
        // 直径 4公里
        wkt = "POLYGON ((108.96140121820795 34.278037809880786,108.92540401820796 34.278037809880786,108.92540401820796 34.24204060988079,108.96140121820795 34.24204060988079,108.96140121820795 34.278037809880786))";
//        areaByMercatorWithWkt = GeometryUtil.getAreaByMercatorWithWkt(wkt);
        areaByWkt = GeometryUtil.getAreaWithWkt(wkt);
        System.out.println(areaByWkt + "____" + areaByMercatorWithWkt);
        // 直径 10公里
        wkt = "POLYGON ((108.98839911820795 34.30503570988079,108.89840611820796 34.30503570988079,108.89840611820796 34.215042709880784,108.98839911820795 34.215042709880784,108.98839911820795 34.30503570988079))";
//        areaByMercatorWithWkt = GeometryUtil.getAreaByMercatorWithWkt(wkt);
        areaByWkt = GeometryUtil.getAreaWithWkt(wkt);
        System.out.println(areaByWkt + "____" + areaByMercatorWithWkt);

        String swkt = "POLYGON ((113.49363295 22.98178600,113.49141745 22.98205516,113.49073617 22.98496151,113.49021046 22.98510473,113.49073617 22.98141066,113.49032713 22.98094026,113.49126255 22.98006056,113.49322794 22.98077111,113.49311931 22.97969818,113.49421231 22.97874377,113.49522082 22.97856597,113.49631516 22.97906972,113.49680869 22.97936605,113.49701254 22.97965249,113.49658339 22.98425530,113.49435179 22.98453186,113.49363295 22.98178600))\",\"POLYGON ((113.49363295 22.98178600,113.49141745 22.98205516,113.49073617 22.98496151,113.49021046 22.98510473,113.49073617 22.98141066,113.49032713 22.98094026,113.49126255 22.98006056,113.49322794 22.98077111,113.49311931 22.97969818,113.49421231 22.97874377,113.49522082 22.97856597,113.49631516 22.97906972,113.49680869 22.97936605,113.49701254 22.97965249,113.49658339 22.98425530,113.49435179 22.98453186,113.49363295 22.98178600))";
        Geometry geometry = GeometryUtil.smoothWithWkt(swkt);
        System.out.println("平滑处理后____" + geometry.toText());

        wkt = "POLYGON ((108.98839911820795 34.30503570988079,108.89840611820796 34.30503570988079,108.89840611820796 34.215042709880784,108.98839911820795 34.215042709880784,108.98839911820795 34.30503570988079))";
        Double lon = 108.89840611820796;
        Double lat = 34.30503570988079;
        boolean sign = GeometryUtil.pointInPolygonWKT(wkt, lon, lat);
        System.out.println("点在面内pointInPolygonWKT: " + sign);

    }

}
